package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.article.dtos.ApArticleConfigDto;
import com.heima.article.pojos.ApArticle;
import com.heima.article.pojos.ApArticleConfig;
import com.heima.comment.dtos.CommentDto;
import com.heima.comment.dtos.CommentLikeDto;
import com.heima.comment.pojos.ApComment;
import com.heima.comment.dtos.CommentSaveDto;
import com.heima.comment.context.BaseContext;
import com.heima.comment.pojos.ApCommentLike;
import com.heima.comment.service.ApCommentService;
import com.heima.comment.vos.CommentVo;
import com.heima.common.constants.CommentConstants;
import com.heima.common.constants.HotArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.feign.article.ArticleFeignClient;
import com.heima.feign.wemedia.WemediaClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.UpdateArticleMess;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service
public class ApCommentServiceImpl implements ApCommentService {

    @Autowired
    private WemediaClient wemediaClient;

    @Autowired
    private ArticleFeignClient articleFeignClient;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    /**
     * 保存评论
     *
     * @param commentSaveDto 前端dto
     */
    @Override
    public void save(CommentSaveDto commentSaveDto) {
        //1 参数校验
        if (commentSaveDto == null ||
                commentSaveDto.getArticleId() == null ||
                commentSaveDto.getContent() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //2 校验是否登录
        int userId = BaseContext.getCurrentId().intValue();
        if (userId == 0) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        //评论内容不能超过140字-自动抛出MethodArgumentNotValidException异常捕获
        //前端已控制-评论内容不能超过140字
        saveArticleComment(commentSaveDto);

        //发送消息给kafkaStream处理
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setType(UpdateArticleMess.UpdateArticleType.COMMENT);
        mess.setArticleId(commentSaveDto.getArticleId());
        mess.setAdd(1);
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
    }

    /**
     * 保存文章评论
     *
     * @param commentSaveDto 前端dto信息
     */
    private void saveArticleComment(CommentSaveDto commentSaveDto) {
        String content = commentSaveDto.getContent();
        ApArticleConfigDto apArticleConfigDto = new ApArticleConfigDto();
        apArticleConfigDto.setArticleId(commentSaveDto.getArticleId());
        if (!getArticleIsComment(apArticleConfigDto)) {
            throw new CustomException(AppHttpCodeEnum.ARTICLE_COMMENT_CLOSE_ERROR);
        }
        //3 文本反垃圾检测
        apCommentContentScan(content);
        //保存文章评论内容到MongoDB中
        saveApCommentToMongoDB(commentSaveDto, content);
    }

    /**
     * 文本反垃圾检测
     *
     * @param content 文章评论内容
     */
    @Override
    public void apCommentContentScan(String content) {
        CommentDto commentDto = new CommentDto();
        commentDto.setContent(content);
        ResponseResult<Boolean> rsScan = wemediaClient.scan(commentDto);
        if (rsScan.getCode() != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }
        Boolean rs = rsScan.getData();
        if (!rs) {
            throw new CustomException(AppHttpCodeEnum.ARTICLE_COMMENT_SENSITIVE_ERROR);
        }
    }

    /**
     * 保存文章评论-MongoDB中
     *
     * @param commentSaveDto 前端dto
     * @param content        评论内容
     */
    private void saveApCommentToMongoDB(CommentSaveDto commentSaveDto, String content) {
        //查询文章作者信息
        Long articleId = commentSaveDto.getArticleId();
        ResponseResult<ApArticle> rsArticle = articleFeignClient.getArticle(articleId);
        if (rsArticle.getCode() != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }
        ApArticle apArticle = rsArticle.getData();
        if (apArticle == null) {
            return;
        }
        //保存评论
        ApComment apComment = new ApComment();
        //id字段使用ObjectId自动生成
        Integer authorId = apArticle.getAuthorId();
        String authorName = apArticle.getAuthorName();
        apComment.setAuthorId(authorId);//被评论的用户id
        apComment.setAuthorName(authorName);//用户昵称
        apComment.setContent(content);
        apComment.setCreatedTime(new Date());
        apComment.setLikes(0);
        apComment.setEntryId(articleId);
        apComment.setReply(0);
        apComment.setType(0);
        apComment.setFlag(0);
        mongoTemplate.save(apComment);
    }

    /**
     * 加载评论
     *
     * @param commentDto 前端dto
     * @return 评论列表
     */
    @Override
    public List<CommentVo> load(CommentDto commentDto) {
        //参数校验
        if (commentDto == null ||
                commentDto.getArticleId() == null ||
                commentDto.getMinDate() == null
        ) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        int userId = BaseContext.getCurrentId().intValue();
        Long articleId = commentDto.getArticleId();
        Date minDate = commentDto.getMinDate();
        //查询评论列表
        List<CommentVo> commentVoList = loadArticleComment(userId, articleId, minDate);
        return commentVoList;
    }


    /**
     * 加载文章评论
     *
     * @param userId    评论的用户
     * @param articleId 评论的文章id
     * @param minDate   分页查询条件 小于最小时间
     * @return 返回结果
     */
    private List<CommentVo> loadArticleComment(int userId, Long articleId, Date minDate) {
        /**
         * 分页查询条件---加载更多:小于最小时间 起始索引:0 显示条数:10
         * 不是按照起始索引进行分页查询,而是按照加载更多小于最小时间进行分页查询
         * where entryId = articleId and createTime <= minDate
         */
        //查看文章是否可评论
        ApArticleConfigDto apArticleConfigDto = new ApArticleConfigDto();
        apArticleConfigDto.setArticleId(articleId);
        if (!getArticleIsComment(apArticleConfigDto)) {
            return null;
        }
        Query commentQuery = Query.query(
                        Criteria.where("entryId").is(articleId)
                                .and("flag").is(CommentConstants.COMMENT_IS_NO)
                                .and("createdTime").lt(minDate)
                )//查询条件
                .with(Sort.by(Sort.Direction.DESC, "createdTime"))//排序条件
                .skip(0)//起始索引:0
                .limit(10);//显示条数:10

        List<ApComment> apComments = mongoTemplate.find(commentQuery, ApComment.class);

        //设置响应结果
        List<CommentVo> commentVoList = apComments.stream().map(apComment -> {
            CommentVo commentVo = new CommentVo();
            BeanUtils.copyProperties(apComment, commentVo);
            commentVo.setCreatedTime(apComment.getCreatedTime().getTime());
            //查询当前用户是否对当前评论点赞(登录用户和未登录用户)
            if (userId != 0) {
                //登录用户,查询点赞状态
                //查询当前用户,对这篇评论是否点赞,如果点赞设置0,否则设置为null
                ApCommentLike commentLike = getApCommentLike(userId, apComment.getId());
                if (commentLike != null) {
                    //已登录,已点赞,设置为0
                    commentVo.setOperation(0);
                }
                //已登录,未点赞,设置为null,直接使用默认值null
            }
            //未登录,未点赞,设置为null,直接使用默认值null
            return commentVo;
        }).collect(Collectors.toList());

        return commentVoList;
    }

    /**
     * 文章是否可评论
     *
     * @param apArticleConfigDto 查询的条件dto
     * @return 返回结果 ture可评论 false不可评论
     */
    private boolean getArticleIsComment(ApArticleConfigDto apArticleConfigDto) {
        ResponseResult<ApArticleConfig> rs = articleFeignClient.getArticleConfig(apArticleConfigDto);
        if (rs.getCode() != AppHttpCodeEnum.SUCCESS.getCode()) {
            throw new CustomException(AppHttpCodeEnum.CALL_INTERFACE_FAIL);
        }
        if (rs.getData() != null) {
            Integer isComment = rs.getData().getIsComment();
            if (isComment != CommentConstants.COMMENT_IS_OK) {
                return false;
            }
        }
        return true;
    }


    /**
     * 评论点赞
     *
     * @param commentLikeDto 前端dto
     * @return 当前评论点赞数量
     */
    @Override
    public Map<String, Integer> like(CommentLikeDto commentLikeDto) {
        //1参数校验
        if (commentLikeDto == null ||
                commentLikeDto.getCommentId() == null ||
                commentLikeDto.getOperation() == null
        ) {
            throw new CustomException(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //2 校验用户是否登录
        int userId = BaseContext.getCurrentId().intValue();
        int likes = 0;//点赞量
        if (userId == 0) {
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }
        String commentId = commentLikeDto.getCommentId();//评论id
        Short operation = commentLikeDto.getOperation();//点赞行为 0点赞 1取消点赞
        //3 点赞---
        if (operation == CommentConstants.LIKE_OPERATION) {
            likes = commentLike(userId, likes, commentId);
        }

        //4 取消点赞---
        if (operation == CommentConstants.CANCEL_LIKE_OPERATION) {
            likes = commentCancelLike(userId, likes, commentId);

        }

        //封装响应结果
        Map<String, Integer> likesMap = new HashMap<>();
        likesMap.put("likes", likes);
        return likesMap;
    }

    /**
     * 文章评论取消点赞
     *
     * @param userId    用户id
     * @param likes     点赞量
     * @param commentId 文章id
     * @return 返回取消评论点赞后的点赞量
     */
    private int commentCancelLike(int userId, int likes, String commentId) {
        //删除用户点赞记录
        ApCommentLike apCommentLike = getApCommentLike(userId, commentId);
        if (apCommentLike != null) {
            removeApCommentLike(userId, commentId);
        }
        //点赞数量-1
        ApComment apComment = getApCommentById(commentId);
        if (apComment != null) {
            likes = apComment.getLikes();
            likes = likes <= 0 ? 0 : likes - 1;
            apComment.setLikes(likes);
            mongoTemplate.save(apComment);
        }
        return likes;
    }

    /**
     * 文章评论点赞
     *
     * @param userId    用户id
     * @param likes     点赞量
     * @param commentId 文章id
     * @return 返回评论点赞后的点赞量
     */
    private int commentLike(int userId, int likes, String commentId) {
        ApCommentLike commentLike = getApCommentLike(userId, commentId);
        //如果没有,说明第一次点赞,直接保存用户点赞记录
        if (commentLike == null) {
            commentLike = new ApCommentLike();
            //id字段使用ObjectId自动生成
            commentLike.setCommentId(commentId);
            commentLike.setAuthorId(userId);
            mongoTemplate.save(commentLike);
        }
        //点赞量+1==>第一次点赞,插入记录,点赞量+1 第二次以后点赞,点赞量+1
        //查询用户对这篇文章的评论记录
        ApComment apComment = getApCommentById(commentId);
        //更新点赞数量
        if (apComment != null) {
            likes = apComment.getLikes();
            likes += 1;
            apComment.setLikes(likes);
            mongoTemplate.save(apComment);
        }
        return likes;
    }

    /**
     * 根据id获取评论信息
     *
     * @param commentId 评论id
     * @return 评论信息
     */
    @Override
    public ApComment getApCommentById(String commentId) {
        Query queryComment = Query.query(
                Criteria.where("_id").is(commentId)
        );
        return mongoTemplate.findOne(queryComment, ApComment.class);
    }

    /**
     * 根据用户id和评论id,删除点赞记录
     *
     * @param userId    用户id
     * @param commentId 评论id
     */
    private void removeApCommentLike(int userId, String commentId) {
        Query queryCommentLike = Query.query(
                Criteria.where("commentId").is(commentId)
                        .and("authorId").is(userId)
        );
        mongoTemplate.remove(queryCommentLike, ApCommentLike.class);
    }

    /**
     * 根据用户id和评论id,查询评论点赞记录
     *
     * @param userId    用户id
     * @param commentId 评论id
     * @return 点赞记录
     */
    private ApCommentLike getApCommentLike(int userId, String commentId) {
        //查询用户对这篇文章的点赞记录
        Query queryCommentLike = Query.query(
                Criteria.where("authorId").is(userId)
                        .and("commentId").is(commentId)
        );
        return mongoTemplate.findOne(queryCommentLike, ApCommentLike.class);
    }
}
